package com.example.mqttdevicesimulator;

import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.alibaba.fastjson.JSON;
import com.example.mqttdevicesimulator.jsonformatinfo.ControlInfo;
import com.example.mqttdevicesimulator.jsonformatinfo.DeviceStatusInfo;
import com.example.mqttdevicesimulator.jsonformatinfo.RegisterInfo;
import com.example.mqttdevicesimulator.jsonformatinfo.RoomBindInfo;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.util.Arrays;

/**
 * Sound box activity, simulating turning on and off of the sound box
 * by change icons.
 */
public class SoundBoxActivity extends AppCompatActivity {

    private ImageView soundBoxIv;
    private Button switchBtn;
    private Boolean isDeviceOn = false;

    private ClientOperation clientOperation;
    private EditText brokerAddressEt;
    private Button connectBtn;
    private SeekBar volumeBar;
    private String device_info;
    private RegisterInfo registerInfo;
    private static final String TAG = "SoundBoxActivity";

    private String room_id;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sound_box);

        Intent intent = getIntent();
        device_info = intent.getStringExtra("device_info");
        registerInfo = JSON.parseObject(device_info, RegisterInfo.class);

        initUI();
    }

    private void initUI() {
        soundBoxIv = findViewById(R.id.sound_box_iv);
        switchBtn = findViewById(R.id.sound_box_switch_btn);
        volumeBar = findViewById(R.id.sound_box_volume_seekbar);

        brokerAddressEt = findViewById(R.id.broker_address_et);
        connectBtn = findViewById(R.id.sound_box_connect_btn);

        switchBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isDeviceOn) {
                    Log.i(TAG, "onClick: going to switch off");
                    turnOffDevice();
                } else {
                    Log.i(TAG, "onClick: going to switch on");
                    turnOnDevice();
                }
            }
        });

        connectBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startMqttService();
            }
        });

        final AudioManager mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        volumeBar.setMax(maxVolume);
        int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        volumeBar.setProgress(currentVolume);
        volumeBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);
                    int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                    seekBar.setProgress(currentVolume);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }

    private void turnOnDevice() {
        Log.i(TAG, "turnOnDevice: turned on");

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                soundBoxIv.setImageResource(R.drawable.sound_box_on);
                switchBtn.setText("Turn Off");
            }
        });

        isDeviceOn = true;

        if (client != null && client.isConnected() && room_id != null) {
            clientOperation.doPublish(UtilConstants.Topic.roomFeedback(room_id), generateDeviceStatusMqttMsg(UtilConstants.DeviceStatus.ON));
        }
    }

    private void turnOffDevice() {
        Log.i(TAG, "turnOffDevice: turned off");

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                soundBoxIv.setImageResource(R.drawable.sound_box_off);
                switchBtn.setText("Turn On");
            }
        });

        isDeviceOn = false;

        if (client != null && client.isConnected() && room_id != null) {
            clientOperation.doPublish(UtilConstants.Topic.roomFeedback(room_id), generateDeviceStatusMqttMsg(UtilConstants.DeviceStatus.OFF));
        }
    }

    private MqttAsyncClient client;
    private String broker;

    /**
     * Create MQTT client, register callback function handling room registration and device control
     */
    public void initClient() throws MqttException {

        clientOperation = new ClientOperation(SoundBoxActivity.this, broker);

        client = clientOperation.createClient();

        client.setCallback(new MqttCallback() {
            @Override
            public void connectionLost(Throwable cause) {
                Log.w(TAG, "connectionLost: start reconnection", cause);
                clientOperation.doConnect();
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                Log.i(TAG, "messageArrived: " + message.toString());
                if (topic.equals(UtilConstants.Topic.ASSIGN_ROOM)) {    // assign the device to a room
                    RoomBindInfo roomBindInfo = JSON.parseObject(message.toString(), RoomBindInfo.class);
                    if (registerInfo.getDevice_id().equals(roomBindInfo.getDevice_id())) {
                        room_id = roomBindInfo.getRoom_id();
                        registerRoom();
                        if (isDeviceOn) {
                            clientOperation.doPublish(UtilConstants.Topic.roomFeedback(room_id), generateDeviceStatusMqttMsg(UtilConstants.DeviceStatus.ON));
                        } else {
                            clientOperation.doPublish(UtilConstants.Topic.roomFeedback(room_id), generateDeviceStatusMqttMsg(UtilConstants.DeviceStatus.OFF));
                        }
                    }
                } else if (room_id != null) {
                    if (topic.equals(room_id)) {  // get controlled
                        ControlInfo controlInfo = JSON.parseObject(message.toString(), ControlInfo.class);
                        if (registerInfo.getDevice_id().equals(controlInfo.getDevice_id())) {
                            switch (controlInfo.getCommand().toLowerCase()) {
                                case UtilConstants.DeviceStatus.ON:
                                    turnOnDevice();
                                    break;
                                case UtilConstants.DeviceStatus.OFF:
                                    turnOffDevice();
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    if (topic.equals(UtilConstants.Topic.roomTouchPad(room_id))) {
                        if (isDeviceOn) {
                            clientOperation.doPublish(UtilConstants.Topic.roomFeedback(room_id), generateDeviceStatusMqttMsg(UtilConstants.DeviceStatus.ON));
                        } else {
                            clientOperation.doPublish(UtilConstants.Topic.roomFeedback(room_id), generateDeviceStatusMqttMsg(UtilConstants.DeviceStatus.OFF));
                        }
                    }
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                Log.i(TAG, "Delivery complete callback: Publish Completed " + Arrays.toString(token.getTopics()));
            }
        });
    }


    /**
     * Send device ID and type to the broker, and register the assign_room topic to bind to a room
     */
    public void registerDevice() {
        MqttMessage message = new MqttMessage(device_info.getBytes());
        message.setQos(UtilConstants.Qos.QOS2);
        registerAssignment();
        clientOperation.doPublish(UtilConstants.Topic.REGISTER, message);
    }

    public void registerRoom() {
        clientOperation.doSubscribe(room_id, UtilConstants.Qos.QOS2);
        clientOperation.doSubscribe(UtilConstants.Topic.roomTouchPad(room_id), UtilConstants.Qos.QOS2);
    }

    public void registerAssignment() {
        clientOperation.doSubscribe(UtilConstants.Topic.ASSIGN_ROOM, UtilConstants.Qos.QOS2);
    }

    public void startMqttService() {

        // check new broker input.
        String curInput = brokerAddressEt.getText().toString();
        if (broker == null || !broker.equals(curInput)) {
            broker = curInput;
            client = null; //to start a new client
        }

        if (client == null) {
            try {
                initClient();
            } catch (MqttException e) {
                Log.e(TAG, "startMqttService: fail to create client", e);
            }
        }

        if (!client.isConnected()) {
            //reconnect for those initialized client instance but connection failed.
            clientOperation.doConnect();

            /**
             *  wait until connector callback invoke.
             */
            synchronized (clientOperation.waiter) {
                try {
                    Log.i(TAG, "wait in connection");

                    // wait for at most maxTTW
                    clientOperation.waiter.wait(clientOperation.maxTTW);
                } catch (InterruptedException e) {
                    Log.e(TAG, "timed out", e);
                }
            }
            if (client.isConnected()) {

                Toast.makeText(SoundBoxActivity.this, "Connected to " + broker,
                        Toast.LENGTH_SHORT).show();
                registerDevice();
            } else {

                Toast.makeText(SoundBoxActivity.this, "Connect failed",
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    private MqttMessage generateDeviceStatusMqttMsg(String status) {
        DeviceStatusInfo deviceStatusInfo = new DeviceStatusInfo();
        deviceStatusInfo.setDevice_id(registerInfo.getDevice_id());
        deviceStatusInfo.setDevice_status(status);
        deviceStatusInfo.setDevice_name(registerInfo.getDevice_name());
        deviceStatusInfo.setDevice_type(registerInfo.getDevice_type());
        String statusInfo = JSON.toJSONString(deviceStatusInfo);
        return new MqttMessage(statusInfo.getBytes());
    }

    @Override
    public void onDestroy() {

        if (isDeviceOn) {
            turnOffDevice();
        }
        if (client != null) {
            if (client.isConnected()) {
                try {
                    client.disconnect();
                    client.close();
                } catch (MqttException e) {
                    Log.e(TAG, "onDestroy: mqtt fail to disconnect", e);
                }
            }
        }

        super.onDestroy();
    }
}
